package com.zxd.interview.util.date;

import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.ChronoField;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 时间帮助类
 * @author zhaoxudong
 */
public abstract class DateHelper {
    private static final Logger logger = LoggerFactory.getLogger(DateHelper.class);
    public static final int DAY_TIME = 24 * 60 * 60 * 1000; // 1天的毫秒数
    public static final String yyyyMM = "yyyy-MM";
    public static final String yyyyMMdd = "yyyy-MM-dd";
    public static final String yyyyMMdd_hhmmss = "yyyy-MM-dd HH:mm:ss";
    public static final String yyyyMMdd_hhmmssSSS = "yyyy-MM-dd HH:mm:ss:SSS";
    public static final String yyyy_MM_dd_HH_mm_ss = "yyyy-MM-dd-HH-mm-ss";
    public static final String yyyyDotMMDotdd = "yyyy.MM.dd";
    public static final String yyyy_MM_dd = "yyyy/MM/dd";
    public static final String START = " 00:00:00";
    public static final String END = " 23:59:59";
    public static final String HH = "HH";

    public static final int xndMonth = 9;//学年度月份

    public static final int xndDay = 1;//学年度月份

    /**
     * "yyyy年MM月"
     */
    public static final String yyyy_mm_dd = "yyyy年MM月";
    /**
     * "yyyy年MM月dd日
     */
    public static final String yyyyNmmYddR = "yyyy年MM月dd日";
    /**
     * yyyy年MM月
     */
    public static final String yyyyNmmR = "yyyy年MM月";
    /**
     * yyyy
     */
    public static final String yyyy = "yyyy";

    private DateHelper() {
        throw new AssertionError("工具类不允许实例化");
    }

    /**
     * 格式化为 format格式 的日期
     *
     * @param date   时间
     * @param format 格式
     * @return 格式化后的日期
     */
    public static String getString(long date, String format) {
        return getString(new Date(date), format);
    }

    public static String getString(Date date, String format) {
        return DateFormatUtils.format(date, format);
    }

    public static String getString(String date, String fromFormat, String toFormat) {
        try {
            return getString(DateUtils.parseDate(date, fromFormat), toFormat);
        } catch (ParseException e) {
            logger.error(date + "解析为" + fromFormat + "格式失败:", e);
        }
        return null;
    }

    /**
     * 获取 format 格式化的当前时间
     *
     * @param format 日期格式
     * @return 当前时间
     * @deprecated 当前方法已被 getnNowByNew() 代替
     */
    @Deprecated
    public static String getNow(String format) {
        SimpleDateFormat sdf = new SimpleDateFormat(format);
        return sdf.format(new Date());
    }

    public static int getNowYear() {
        return Calendar.getInstance().get(Calendar.YEAR);
    }

    public static Date getNowYearStart() {
        return getYearStart(getNowYear());
    }

    public static Date getNowYearEnd() {
        return getYearEnd(getNowYear());
    }

    public static int getNowMonth() {
        return Calendar.getInstance().get(Calendar.MONTH) + 1;
    }

    public static int getNowDay() {
        return Calendar.getInstance().get(Calendar.DAY_OF_MONTH);
    }

    /**
     * 将 format形式的date字符串 转化为 long 格式的日期
     *
     * @param date   日期字符串
     * @param format 格式
     * @return 返回自1970年1月1日00:00:00 GMT以来的毫秒数
     */
    public static long getTime(String date, String format) {
        try {
            SimpleDateFormat sdf = new SimpleDateFormat(format);
            return sdf.parse(date).getTime();
        } catch (ParseException e) {
            logger.error("时间解析异常:" + date, e);
        }
        return -1L;
    }

    /**
     * 将 format形式的date字符串 转化为 Date 格式的日期
     *
     * @param date   日期字符串
     * @param format 格式
     */
    public static Date getDate(String date, String format) {
        try {
            return DateUtils.parseDateStrictly(date, format);
        } catch (ParseException e) {
            logger.error("时间解析异常:" + date, e);
        }
        return null;
    }

    /**
     * 判断 date日期 是否在 start日期 和 end日期 之间
     *
     * @param date  判断日期
     * @param start 开始日期下限
     * @param end   结束日期上限
     * @return 判断 date日期 是否在 start日期 和 end日期 之间
     */
    public static boolean isBetween(Date date, Date start, Date end) {
        return isBetween(date.getTime(), start.getTime(), end.getTime());
    }

    public static boolean isBetween(long date, long start, long end) {
        return start <= date && date <= end;
    }

    /**
     * 获取开始日期和结束日期的间隔
     * 注意：如果不满足进位会被四舍五入为0，比如不足一小时设置为0
     * @param start    开始日期
     * @param end      结束日期
     * @param timeUnit 转化后的时间格式, 天数、秒数、毫秒数等
     * @return 计算两个日期的时间间隔
     */
    public static long getBetween(long start, long end, TimeUnit timeUnit) {
        return Math.abs(timeUnit.convert(end - start, TimeUnit.MILLISECONDS));
    }

    public static Date getYearStart(int year) {
        Calendar cal = Calendar.getInstance();
        cal.set(year, Calendar.JANUARY, 1, 0, 0, 0);
        return cal.getTime();
    }

    public static Date getYearEnd(int year) {
        Calendar cal = Calendar.getInstance();
        cal.set(year, Calendar.DECEMBER, 31, 23, 59, 59);
        return cal.getTime();
    }

    public static Date getMonthStart(int year, int month) {
        Calendar cal = Calendar.getInstance();
        cal.set(year, month - 1, 1, 0, 0, 0);
        return cal.getTime();
    }

    public static Date getMonthEnd(int year, int month) {
        Calendar cal = Calendar.getInstance();
        cal.set(year, month - 1, 1, 23, 59, 59);
        cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH));
        return cal.getTime();
    }

    /**
     * 获取日期
     * @param date 判断日期类型格式
     *             导入日期格式分别为"yyyy-MM-dd""yyyy.MM.dd""yyyy/MM/dd"
     * @return
     */
    public static long getTime(String date) {
        String format = null;
        if (date.contains("-")) {
            format = yyyyMMdd;
        }
        if (date.contains(".")) {
            format = yyyyDotMMDotdd;
        }
        if (date.contains("/")) {
            format = yyyy_MM_dd;
        }
        return getTime(date, format);
    }

    /**
     * 分割年份，根据当前时间及指定月份确认所属年份
     * 指定某一个月份划分年份
     * <p>例如：设置5，则5月之前为上一年，5月之后为下一年</p>
     * @param splitMonth 分割月份
     * @return
     */
    public static int getSplitCurrentYear(int splitMonth) {
        int curYear = getNowYear();
        int curMonth = getNowMonth();
        if (curMonth < splitMonth) {
            curYear--;
        }
        return curYear;
    }

    /**
     * 以9月划分,计算年份,通常用于计算学校学期年度
     *
     * @return
     */
    public static int callSchoolYear() {
        Calendar calendar = Calendar.getInstance();
        //获取当前年份
        int curYear = calendar.get(Calendar.YEAR);
        //获取当前月份
        int month = calendar.get(Calendar.MONTH);
        //当前月份小于九月,年份减1
        if (month < Calendar.SEPTEMBER) {
            curYear--;
        }
        return curYear;
    }

    /**
     * 从当前年度开始计算,多少年前的年份集合。包含当前年份
     * howYears = 5 ,当前年份2021  :  [2021, 2020, 2019, 2018, 2017]
     * howYears 多少年
     *
     * @return
     */
    public static List<Integer> getYearsBefore(int howYears) {
        List<Integer> years = new ArrayList<>();
        //获取当前年份
        Calendar calendar = Calendar.getInstance();
        int curYear = calendar.get(Calendar.YEAR);
        //遍历howYears,获取之前多少年的年份,通过当前年份遍历递减
        for (int i = 0; i < howYears; i++) {
            years.add(curYear - i);
        }
        return years;
    }

    /**
     * 获取月份集合
     * @return
     */
    public static List<Integer> getAllMonths() {
        Integer[] time = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
        return Arrays.asList(time);
    }


    /**
     * 判断当前时间是否大于目标时间
     * 格式默认为 'yyyy-MM-dd'
     *
     * @param time 时间字符串
     * @return 判断当前时间是否大于目标时间
     */
    public static boolean isAfter(String time) {
        return !isBefore(time, DateHelper.yyyyMMdd);
    }

    /**
     * 判断当前时间是否大于目标时间
     *
     * @param time   时间字符串
     * @param format 指定格式
     * @return 判断当前时间是否大于目标时间
     */
    public static boolean isAfter(String time, String format) {
        return !isBefore(time, format);
    }

    /**
     * 使用jdk 1.8 的日期类进行比较时间
     * 判断当前时间是否大于目标时间
     * 格式默认为 'yyyy-MM-dd'
     *
     * @param time 时间字符串
     * @return 判断当前时间是否小于目标时间
     */
    public static boolean isBefore(String time) {
        return isBefore(time, DateHelper.yyyyMMdd);
    }

    /**
     * 使用jdk 1.8 的日期类进行比较时间
     * 判断当前时间是否小于目标时间
     *
     * @param time   时间字符串
     * @param format 指定格式
     * @return 判断当前时间是否小于目标时间
     */
    public static boolean isBefore(String time, String format) {
        DateTimeFormatter dateTimeFormatter = generateDefualtPattern(format);
        LocalDateTime compareTime = LocalDateTime.parse(time, dateTimeFormatter);
        LocalDateTime current = LocalDateTime.parse(getNowByNew(format), dateTimeFormatter);
        long compare = Instant.from(compareTime.atZone(ZoneId.systemDefault())).toEpochMilli();
        long currentTimeMillis = Instant.from(current.atZone(ZoneId.systemDefault())).toEpochMilli();
        return currentTimeMillis < compare;
    }

    /**
     * 比较前一个时间是否小于后一个时间
     *
     * @param compare1      前一个时间
     * @param compare2      后一个时间
     * @param formatPattern 指定日期格式
     * @return 比较前一个时间是否小于后一个时间
     */
    public static boolean isBefore(String compare1, String compare2, String formatPattern) {
        DateTimeFormatter dateTimeFormatter = generateDefualtPattern(formatPattern);
        long compare1Start1 = getTimeMillions(compare1, dateTimeFormatter);
        long compare1Start2 = getTimeMillions(compare2, dateTimeFormatter);
        if (compare1Start1 < compare1Start2) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 比较前一个时间是否大于后一个时间
     *
     * @param compare1      前一个时间
     * @param compare2      后一个时间
     * @param formatPattern 指定日期格式
     * @return 比较前一个时间是否小于后一个时间
     */
    public static boolean isAfter(String compare1, String compare2, String formatPattern) {
        return !isBeforeOrEq(compare1, compare2, formatPattern);
    }

    /**
     * 前一个时间 时间 小于 或者 等于 后一个时间
     *
     * @param compare1      前一个时间
     * @param compare2      后一个时间
     * @param formatPattern 格式
     * @return
     */
    public static boolean isBeforeOrEq(String compare1, String compare2, String formatPattern) {
        return isBefore(compare1, compare2, formatPattern) || isEq(compare1, compare2, formatPattern);
    }

    /**
     * 前一个时间 时间 大于 或者 等于 后一个时间
     *
     * @param compare1      前一个时间
     * @param compare2      后一个时间
     * @param formatPattern 格式
     * @return
     */
    public static boolean isAfterOrEq(String compare1, String compare2, String formatPattern) {
        return !isBefore(compare1, compare2, formatPattern);
    }

    /**
     * 根据指定格式获取指定时间的前N天
     *
     * @param time       当前时间
     * @param timeFormat 格式化 字符串
     * @param minuDays   前N天
     * @return
     */
    public static String minuDayByFormat(String time, long minuDays, String timeFormat) {
        DateTimeFormatter dateTimeFormatter = generateDefualtPattern(timeFormat);
        return LocalDateTime.parse(time, dateTimeFormatter).minusDays(minuDays).format(dateTimeFormatter);
    }

    /**
     * 根据指定格式获取指定时间的前N天
     *
     * @param time       当前时间
     * @param timeFormat 格式化 字符串
     * @param minuDays   前N天
     * @return
     */
    public static String minuDayByFormat(Date time, long minuDays, String timeFormat) {
        DateTimeFormatter dateTimeFormatter = generateDefualtPattern(timeFormat);
        return LocalDateTime.parse(DateHelper.getString(time, timeFormat), dateTimeFormatter).minusDays(minuDays).format(dateTimeFormatter);
    }

    /**
     * 根据指定格式获取指定时间的后N天
     *
     * @param time       当前时间
     * @param timeFormat 格式化 字符串
     * @param plusDays   前N天
     * @return
     */
    public static String plusDayByFormat(String time, long plusDays, String timeFormat) {
        DateTimeFormatter dateTimeFormatter = generateDefualtPattern(timeFormat);
        return LocalDateTime.parse(time, dateTimeFormatter).plusDays(plusDays).format(dateTimeFormatter);
    }

    /**
     * 根据指定格式获取指定时间的后N天
     *
     * @param time       当前时间
     * @param timeFormat 格式化 字符串
     * @param plusDays   前N天
     * @return
     */
    public static String plusDayByFormat(Date time, long plusDays, String timeFormat) {
        DateTimeFormatter dateTimeFormatter = generateDefualtPattern(timeFormat);
        return LocalDateTime.parse(DateHelper.getString(time, timeFormat), dateTimeFormatter).plusDays(plusDays).format(dateTimeFormatter);
    }

    /**
     * 获取当前时间
     *
     * @param timeFormat 指定格式
     * @return 时间
     */
    public static String getNowByNew(String timeFormat) {
        return LocalDateTime.now().format(generateDefualtPattern(timeFormat));
    }


    /**
     * 获取当前的天数信息
     *
     * @param date   时间
     * @param format 格式化参数
     * @return
     */
    public static int getDayTime(Date date, String format) {
        DateTimeFormatter dateTimeFormatter = generateDefualtPattern(format);
        return LocalDate.parse(DateHelper.getString(date, format), dateTimeFormatter).getDayOfMonth();
    }

    /**
     * 生成默认的格式器
     *
     * @param timeFormat 指定格式
     * @return 默认时间格式器
     */
    private static DateTimeFormatter generateDefualtPattern(String timeFormat) {
        return new DateTimeFormatterBuilder().appendPattern(timeFormat)
                .parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
                .parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
                .parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0)
                .toFormatter(Locale.CHINA);
    }

    /**
     * 获取时间毫秒值
     *
     * @param dateTimeFormatter 格式器
     * @param time              时间
     * @return
     */
    private static long getTimeMillions(String time, DateTimeFormatter dateTimeFormatter) {
        LocalDateTime compareTime1 = LocalDateTime.parse(time, dateTimeFormatter);
        return Instant.from(compareTime1.atZone(ZoneId.systemDefault())).toEpochMilli();
    }


    /**
     * 校验两个时间是否相等
     *
     * @param compare1       第一个时间
     * @param compare2       第二个时间
     * @param formattPattern 时间格式
     * @return
     */
    public static boolean isEq(String compare1, String compare2, String formattPattern) {
        DateTimeFormatter dateTimeFormatter = generateDefualtPattern(formattPattern);
        long compare1Start1 = getTimeMillions(compare1, dateTimeFormatter);
        long compare1Start2 = getTimeMillions(compare2, dateTimeFormatter);
        if (compare1Start1 == compare1Start2) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 获取指定时间属于星期几
     * 返回枚举对象
     *
     * @param date           日期
     * @param formattPattern 格式
     * @return
     */
    public static DayOfWeek getDayOfWeek(String date, String formattPattern) {
        DateTimeFormatter dateTimeFormatter = generateDefualtPattern(formattPattern);
        return LocalDate.parse(date, dateTimeFormatter).getDayOfWeek();
    }

    /**
     * 获取指定时间属于星期几
     * 返回枚举对象
     *
     * @param date           日期对象
     * @param formattPattern 格式
     * @return
     */
    public static DayOfWeek getDayOfWeek(Date date, String formattPattern) {
        return getDayOfWeek(DateHelper.getString(date, formattPattern), formattPattern);
    }

    /**
     * 获取当前时间对应的月份
     *
     * @param time   时间
     * @param format 格式化
     * @return
     */
    public static int getMonthTimeInt(Date time, String format) {
        DateTimeFormatter dateTimeFormatter = generateDefualtPattern(format);
        return LocalDate.parse(DateHelper.getString(time, format), dateTimeFormatter).getMonthValue();
    }

    /**
     * 获取当前时间对应的年份
     *
     * @param time   时间
     * @param format 格式化
     * @return
     */
    public static int getYearTimeInt(Date time, String format) {
        DateTimeFormatter dateTimeFormatter = generateDefualtPattern(format);
        return LocalDate.parse(DateHelper.getString(time, format), dateTimeFormatter).getYear();
    }

    /**
     * 获取时间的小时数
     *
     * @param date   时间
     * @param format 格式化
     * @return
     */
    public static int getHourTimeInt(Date date, String format) {
        DateTimeFormatter dateTimeFormatter = generateDefualtPattern(format);
        return LocalDateTime.parse(DateHelper.getString(date, format), dateTimeFormatter).getHour();
    }

    /**
     * 获取开始日期和结束日期之间的日期（返回List<String>）
     *
     * @param startTime 开始日期
     * @param endTime   结束日期
     * @return 开始与结束之间的所以日期，包括起止
     */
    public static List<String> getMiddleDateToString(String startTime, String endTime) {
        LocalDate begin = LocalDate.parse(startTime, DateTimeFormatter.ofPattern(yyyyMMdd));
        LocalDate end = LocalDate.parse(endTime, DateTimeFormatter.ofPattern(yyyyMMdd));
        List<LocalDate> localDateList = new ArrayList<>();
        long length = end.toEpochDay() - begin.toEpochDay();
        for (long i = length; i >= 0; i--) {
            localDateList.add(end.minusDays(i));
        }
        List<String> resultList = new ArrayList<>();
        for (LocalDate temp : localDateList) {
            resultList.add(temp.toString());
        }
        return resultList;
    }

    /**
     * 获取开始日期和结束日期之间的日期（返回List<Date>）
     *
     * @param startTime 开始日期
     * @param endTime   结束日期
     * @return 开始与结束之间的所以日期，包括起止
     */
    public static List<Date> getMiddleDateToDate(String startTime, String endTime) {
        LocalDate begin = LocalDate.parse(startTime, DateTimeFormatter.ofPattern(yyyyMMdd));
        LocalDate end = LocalDate.parse(endTime, DateTimeFormatter.ofPattern(yyyyMMdd));
        List<LocalDate> localDateList = new ArrayList<>();
        long length = end.toEpochDay() - begin.toEpochDay();
        for (long i = length; i >= 0; i--) {
            localDateList.add(end.minusDays(i));
        }
        List<Date> resultList = new ArrayList<>();
        for (LocalDate temp : localDateList) {
            //将localDate转化为Date
            ZonedDateTime zonedDateTime = temp.atStartOfDay(ZoneId.systemDefault());
            Instant instant1 = zonedDateTime.toInstant();
            Date curDate = Date.from(instant1);
            resultList.add(curDate);
        }
        return resultList;
    }

    /**
     * LocalDate 转 Date
     * 类表示一个具体的日期，但==不包含==具体时间，也==不包含时区信息==。
     * 可以通过`LocalDate`的静态方法`of()`创建一个实例，
     * `LocalDate`也包含一些方法用来获取年份，月份，天，星期几等
     *
     * @param localDate 时间 ， 不包含具体的日期
     * @return
     */
    public static Date localDate2Date(LocalDate localDate) {
        if (null == localDate) {
            return null;
        }
        ZonedDateTime zonedDateTime = localDate.atStartOfDay(ZoneId.systemDefault());
        return Date.from(zonedDateTime.toInstant());
    }

    /**
     * Date 转 LocalDate
     *
     * @param date jaava.util.date
     * @return
     */
    public static LocalDate date2LocalDate(Date date) {
        if (null == date) {
            return null;
        }
        return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
    }

    /**
     * 获取开始和结束日期中间的相差的天数，如果开始日期小于结束日期则返回 -1
     *
     * @param beginDate 开始日期
     * @param endDate   结束日期
     * @return
     */
    public static long countDay(Date beginDate, Date endDate) {
        LocalDate beginLocalDate = date2LocalDate(beginDate);
        LocalDate endLocalDate = date2LocalDate(endDate);
        if (beginLocalDate.isAfter(endLocalDate)) {
            throw new RuntimeException("开始时间不能晚于结束时间");
        }
        return endLocalDate.toEpochDay() - beginLocalDate.toEpochDay();
    }




}
